﻿using Hylasoft.Opc.Common;
using Hylasoft.Opc.Da;
using Hylasoft.Opc.Ua;
using Microsoft.Extensions.DependencyInjection;
using Opc.Ua;
using Opc.Ua.Configuration;
using SD.Infrastructure.CrontabBase.Mediator;
using SD.IOC.Core.Mediators;
using SD.IOC.Extension.NetFx;
using SD.OpcProxy.Domain.Entities;
using SD.OpcProxy.OpcService.Base;
using SD.OpcProxy.OpcService.Crontabs;
using SD.OpcProxy.OpcService.Proxy;
using SD.OpcProxy.Repository.Base;
using SD.OpcProxy.ValueObjects;
using SD.OpcProxy.ValueObjects.Enums;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace SD.OpcProxy.OpcService.Extensions
{
    /// <summary>
    /// OPC代理扩展
    /// </summary>
    public static class OpcProxyExtension
    {
        #region # 字段及构造器

        /// <summary>
        /// OPC客户端字典
        /// </summary>
        private static readonly IDictionary<string, (OpcServer, IOpcClient)> _Clients;

        /// <summary>
        /// 代理服务器地址
        /// </summary>

        private static string _ProxyServerUrl;

        /// <summary>
        /// 代理客户端
        /// </summary>
        private static IOpcClient _ProxyClient;

        /// <summary>
        /// 代理变量组字典
        /// </summary>
        private static readonly IDictionary<string, ProxyVariableGroup> _ProxyVariableGroups;

        /// <summary>
        /// 代理变量字典
        /// </summary>
        private static readonly IDictionary<string, ProxyVariable> _ProxyVariables;

        /// <summary>
        /// 代理变量键字典
        /// </summary>
        private static readonly IDictionary<string, NodeId> _ProxyVariableKeys;

        /// <summary>
        /// 静态构造器
        /// </summary>
        static OpcProxyExtension()
        {
            _Clients = new ConcurrentDictionary<string, (OpcServer, IOpcClient)>();
            _ProxyVariableGroups = new ConcurrentDictionary<string, ProxyVariableGroup>();
            _ProxyVariables = new ConcurrentDictionary<string, ProxyVariable>();
            _ProxyVariableKeys = new ConcurrentDictionary<string, NodeId>();
        }

        #endregion

        #region # 属性

        #region OPC客户端访问者 —— static {string, (OpcServer OpcServer, IOpcClient OpcClient)} ClientVisitor
        /// <summary>
        /// OPC客户端访问者
        /// </summary>
        /// <remarks>键：OPC服务器地址，值：OPC服务端/客户端</remarks>
        public static IDictionary<string, (OpcServer OpcServer, IOpcClient OpcClient)> ClientVisitor
        {
            get { return _Clients; }
        }
        #endregion

        #region 代理服务器地址 —— static string ProxyServerUrl
        /// <summary>
        /// 代理服务器地址
        /// </summary>
        public static string ProxyServerUrl
        {
            get { return _ProxyServerUrl; }
        }
        #endregion

        #region 代理客户端 —— static IOpcClient ProxyClient
        /// <summary>
        /// 代理客户端
        /// </summary>
        public static IOpcClient ProxyClient
        {
            get { return _ProxyClient; }
        }
        #endregion

        #region 代理变量组字典 —— static {string, ProxyVariableGroup} ProxyVariableGroups
        /// <summary>
        /// 代理变量组字典
        /// </summary>
        /// <remarks>键：OPC服务器地址，值：代理变量组</remarks>
        public static IDictionary<string, ProxyVariableGroup> ProxyVariableGroups
        {
            get { return _ProxyVariableGroups; }
        }
        #endregion

        #region 代理变量字典 —— static {string, ProxyVariable} ProxyVariables
        /// <summary>
        /// 代理变量字典
        /// </summary>
        /// <remarks>键：变量编号，值：代理变量</remarks>
        public static IDictionary<string, ProxyVariable> ProxyVariables
        {
            get { return _ProxyVariables; }
        }
        #endregion

        #region 代理变量键字典 —— static {string, NodeId} ProxyVariableKeys
        /// <summary>
        /// 代理变量键字典
        /// </summary>
        /// <remarks>键：变量编号，值：代理变量节点Id</remarks>
        public static IDictionary<string, NodeId> ProxyVariableKeys
        {
            get { return _ProxyVariableKeys; }
        }
        #endregion

        #endregion

        #region # 方法

        #region 初始化依赖注入 —— static void InitializeDependencies()
        /// <summary>
        /// 初始化依赖注入
        /// </summary>
        public static void InitializeDependencies()
        {
            if (!ResolveMediator.ContainerBuilt)
            {
                IServiceCollection builder = ResolveMediator.GetServiceCollection();
                builder.RegisterConfigs();

                ResolveMediator.Build();
            }
        }
        #endregion

        #region 初始化代理配置 —— static void InitializeProxyConfigurations()
        /// <summary>
        /// 初始化代理配置
        /// </summary>
        public static void InitializeProxyConfigurations()
        {
            const string internalVariableGroupName = "内部变量";

            using (DbSession dbSession = new DbSession())
            {
                //外部变量
                foreach (OpcServer opcServer in dbSession.Set<OpcServer>().Where(x => x.Id != Constants.OpcProxyServerId))
                {
                    ProxyVariableGroup proxyVariableGroup = new ProxyVariableGroup(opcServer.Name, opcServer.Number);

                    IQueryable<Variable> externalVariables = dbSession.Set<Variable>().Where(x => x.OpcServerId == opcServer.Id);
                    IList<ProxyVariable> proxyVariables = new List<ProxyVariable>();

                    foreach (Variable externalVariable in externalVariables)
                    {
                        string variableNo = externalVariable.Number;
                        string variableName = externalVariable.Name;
                        string variableDescription = $"{externalVariable.Name}|{externalVariable.Description}";

                        ProxyVariable proxyVariable = new ProxyVariable(opcServer.Number, variableNo, variableName, externalVariable.Unit, externalVariable.IsReadonly, externalVariable.KeepRecord, variableDescription, externalVariable.ValueDescriptions, proxyVariableGroup);
                        proxyVariables.Add(proxyVariable);
                    }

                    proxyVariableGroup.ProxyVariables = proxyVariables;
                    _ProxyVariableGroups.Add(opcServer.Number, proxyVariableGroup);
                }

                //内部变量
                ProxyVariableGroup internalVariableGroup = new ProxyVariableGroup(internalVariableGroupName, internalVariableGroupName);
                IQueryable<Variable> internalVariables = dbSession.Set<Variable>().Where(x => x.VariableType == VariableType.Internal);
                IList<ProxyVariable> internalProxyVariables = new List<ProxyVariable>();

                foreach (Variable internalVariable in internalVariables)
                {
                    string variableNo = internalVariable.Number;
                    string variableName = internalVariable.Name;
                    string variableDescription = $"{internalVariable.Name}|{internalVariable.Description}";

                    ProxyVariable internalProxyVariable = new ProxyVariable(ProxyServerUrl, variableNo, variableName, internalVariable.Unit, internalVariable.IsReadonly, internalVariable.KeepRecord, variableDescription, internalVariable.ValueDescriptions, internalVariableGroup);
                    internalProxyVariables.Add(internalProxyVariable);
                }

                internalVariableGroup.ProxyVariables = internalProxyVariables;
                _ProxyVariableGroups.Add(ProxyServerUrl, internalVariableGroup);

                //添加变量到字典
                foreach (ProxyVariable proxyVariable in _ProxyVariableGroups.Values.SelectMany(x => x.ProxyVariables))
                {
                    _ProxyVariables.Add(proxyVariable.Number, proxyVariable);
                    _ProxyVariableKeys.Add(proxyVariable.Number, proxyVariable.NodeId);
                }
            }
        }
        #endregion

        #region 初始化代理客户端 —— static void InitializeProxyClient(string url)
        /// <summary>
        /// 初始化代理客户端
        /// </summary>
        /// <param name="url">代理服务器地址</param>
        public static void InitializeProxyClient(string url)
        {
            Uri uri = new Uri(url);
            _ProxyServerUrl = url;
            _ProxyClient = new UaClient(uri);
        }
        #endregion

        #region 初始化代理调度 —— static void InitializeProxySchedule()
        /// <summary>
        /// 初始化代理调度
        /// </summary>
        public static void InitializeProxySchedule()
        {
            //清理
            FinalizeProxySchedule();

            using (DbSession dbSession = new DbSession())
            {
                foreach (OpcServer opcServer in dbSession.Set<OpcServer>().Where(x => x.Id != Constants.OpcProxyServerId))
                {
                    //Ping服务器
                    PingCrontab pingCrontab = new PingCrontab(opcServer.Number, 10);
                    ScheduleMediator.Schedule(pingCrontab);
                }
            }
        }
        #endregion

        #region 清理代理调度 —— static void FinalizeProxySchedule()
        /// <summary>
        /// 清理代理调度
        /// </summary>
        public static void FinalizeProxySchedule()
        {
            //清空定时任务
            ScheduleMediator.Clear();

            //释放OPC客户端
            foreach ((OpcServer OpcServer, IOpcClient OpcClient) kv in ClientVisitor.Values)
            {
                TimeSpan timeout = new TimeSpan(0, 0, 0, kv.OpcServer.Timeout);
                Task.Run(() => kv.OpcClient.Dispose()).Wait(timeout);
            }
        }
        #endregion

        #region 初始化代理服务 —— static void InitializeProxyService(ApplicationInstance opcApplication)
        /// <summary>
        /// 初始化代理服务
        /// </summary>
        /// <param name="opcApplication">OPC应用实例</param>
        public static void InitializeProxyService(ApplicationInstance opcApplication)
        {
            //加载配置
            opcApplication.LoadApplicationConfiguration(Constants.OpcServerConfigPath, false);

            //初始化代理客户端
            string proxyServerUrl = opcApplication.ApplicationConfiguration.ServerConfiguration.BaseAddresses[0];
            InitializeProxyClient(proxyServerUrl);

            //初始化代理配置
            InitializeProxyConfigurations();

            //认证配置
            opcApplication.CheckApplicationInstanceCertificate(false, 0);

            //启动服务
            OpcServiceHost serviceHost = new OpcServiceHost();
            opcApplication.Start(serviceHost);

            //初始化代理调度
            InitializeProxySchedule();

            //代理客户端连接
            ProxyClient.Connect();
        }
        #endregion

        #region 初始化代理适配器 —— static void InitializeProxyAdapters()
        /// <summary>
        /// 初始化代理适配器
        /// </summary>
        public static void InitializeProxyAdapters()
        {
            //适配
            IEnumerable<IProxyAdapter> adapters = ResolveMediator.ResolveAll<IProxyAdapter>();
            foreach (IProxyAdapter adapter in adapters)
            {
                adapter.Adapt();
            }
        }
        #endregion

        #region 获取OPC客户端 —— static IOpcClient GetClient(this OpcServer opcServer)
        /// <summary>
        /// 获取OPC客户端
        /// </summary>
        /// <param name="opcServer">OPC服务器</param>
        /// <returns>OPC客户端</returns>
        public static IOpcClient GetClient(this OpcServer opcServer)
        {
            IOpcClient opcClient;
            Uri uri = new Uri(opcServer.Number);

            if (opcServer.Mode == OpcMode.UA)
            {
                opcClient = new UaClient(uri);
            }
            else if (opcServer.Mode == OpcMode.DA)
            {
                opcClient = new DaClient(uri);
            }
            else
            {
                throw new NotImplementedException("未知OPC模式");
            }

            opcClient.MonitorInterval = opcServer.MonitorInterval;

            return opcClient;
        }
        #endregion

        #endregion
    }
}
